#include "common.h"

// 读取报文预设大小的字节，readn 调用会一直循环，尝试读取预设大小的字节，
// 如果接收缓冲区数据空，readn 函数会阻塞在那里，直到有数据到达
size_t readn(int fd, void *buffer, size_t size) 
{
    char *buffer_pointer = buffer;
    int length = size;  //表示还需要读取的字符数

    while (length > 0) 
    {
        int result = read(fd, buffer_pointer, length); //通过 read 函数来服务最多 length 个字符

        // 行针对返回值进行出错判断，其中返回值为 0 的情形是 EOF，表示对方连接终止
        if (result < 0) 
        {
            if (errno == EINTR)
                continue;     /* 考虑非阻塞的情况，这里需要再次调用read */
            else
                return (-1);
        } 
        else if (result == 0)
            break;                /* EOF(End of File)表示套接字关闭 */

        // 要读取的字符数减去这次读到的字符数，同时移动缓冲区指针，这样做的目的是为了确认字符数是否已经读取完毕
        length -= result;
        buffer_pointer += result;
    }
    return (size - length);        /* 返回的是实际读取的字节数*/
}


size_t readline_2(int fd, char *buffer, size_t length) {
    char *buf_first = buffer;

    char c;
    while (length > 0 && recv(fd, &c, 1, 0) == 1) {
        *buffer++ = c;
        length--;
        if (c == '\n') {
            *buffer = '\0';
            return buffer - buf_first;
        }
    }

    return -1;

}

/* readline - read a newline terminated record */
/* 123456789\n */
size_t readline(int fd, char *buffer, size_t length) 
{
    char *buf_first = buffer;
    static char *buffer_pointer;
    int nleft = 0;
    static char read_buffer[512];
    char c;

    while (--length > 0) //通过对 length 变量的判断，试图解决缓冲区长度溢出问题
    {
        // nread == 0
        if (nleft <= 0) //判断临时缓冲区的字符有没有被全部拷贝完，如果被全部拷贝完，就会再次尝试读取最多 512 字节
        {
            int nread = recv(fd, read_buffer, sizeof(read_buffer), 0);
            if (nread < 0) 
            {
                if (errno == EINTR) 
                {
                    length++;
                    continue;
                }
                return -1;
            }
            if (nread == 0)
                return 0;
            //在读取字符成功之后，重置了临时缓冲区读指针、临时缓冲区待读的字符个数
            buffer_pointer = read_buffer; 
            nleft = nread;
        }
        //在拷贝临时缓冲区字符，每次拷贝一个字符，并移动临时缓冲区读指针，对临时缓冲区待读的字符个数进行减 1 操作
        c = *buffer_pointer++; 
        *buffer++ = c;
        nleft--;
        // 判断是否读到换行符，如果读到则将应用程序最终缓冲区截断，返回最终读取的字符个数
        if (c == '\n') 
        {
            *buffer = '\0';
            return buffer - buf_first;
        }
    }
    return -1;
}

size_t read_message(int fd, char *buffer, size_t length) 
{
    u_int32_t msg_length;
    u_int32_t msg_type;
    int rc;

    /* Retrieve the length of the record */
    // 通过调用 readn 函数获取 4 个字节的消息长度数据
    rc = readn(fd, (char *) &msg_length, sizeof(u_int32_t)); //读取报文预设大小的字节
    if (rc != sizeof(u_int32_t))
        return rc < 0 ? -1 : 0;
    msg_length = ntohl(msg_length);

    // 通过调用 readn 函数获取 4 个字节的消息类型数据
    rc = readn(fd, (char *) &msg_type, sizeof(msg_type));
    if (rc != sizeof(u_int32_t))
        return rc < 0 ? -1 : 0;

    /* 判断buffer是否可以容纳下数据  */ 
    //判断消息的长度是不是太大，如果大到本地缓冲区不能容纳，则直接返回错误
    if (msg_length > length) 
    {
        return -1;
    }

    /* Retrieve the record itself */
    //调用 readn 一次性读取已知长度的消息体
    rc = readn(fd, buffer, msg_length);
    if (rc != msg_length)
        return rc < 0 ? -1 : 0;
    return rc;
}

//在尝试读取一行数据，也就是读到回车符\r，或者读到回车换行符\r\n为止
int read_line(int fd, char *buf, int size) 
{
    int i = 0;
    char c = '\0';
    int n;

    while ((i < size - 1) && (c != '\n')) 
    {
        n = recv(fd, &c, 1, 0); //每次尝试读取一个字节
        if (n > 0) 
        {
            if (c == '\r') //如果读到了回车符\r
            {
                n = recv(fd, &c, 1, MSG_PEEK);
                if ((n > 0) && (c == '\n')) //“观察”下看有没有换行符
                    recv(fd, &c, 1, 0); //读取这个换行符
                else
                    c = '\n';
            }
            buf[i] = c; //如果没有读到回车符，将字符放到缓冲区，并移动指针
            i++;
        } 
        else
            c = '\n';
    }
    buf[i] = '\0';

    return (i);
}
